use super::Result;
use crate::entity::prelude::{SysRole, SysRoleResource};
use crate::entity::{sys_resource, sys_role, sys_role_resource, sys_user, sys_user_role};
use sea_orm::{
    ActiveModelTrait, ColumnTrait, ConnectionTrait, DbConn, DerivePartialModel, EntityTrait,
    FromQueryResult, PaginatorTrait, PartialModelTrait, QueryFilter, QuerySelect, QueryTrait, Set,
};
use serde::{Deserialize, Serialize};
use validator::Validate;

pub struct Role;

/// 角色列表展示的数据
#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SysRole")]
pub struct RoleVo {
    pub id: i32,
    pub name: String,
    pub key: String,
    pub description: String,
    pub data_scope: i8,
    pub status: bool,
}

impl From<sys_role::Model> for RoleVo {
    fn from(value: sys_role::Model) -> Self {
        Self {
            id: value.id,
            name: value.name,
            key: value.key,
            description: value.description,
            data_scope: value.data_scope,
            status: value.status,
        }
    }
}

/// 角色简单的展示的数据
#[derive(Debug, Clone, DerivePartialModel, FromQueryResult, Deserialize, Serialize)]
#[sea_orm(entity = "SysRole")]
pub struct RoleSimpletVo {
    pub id: i32,
    pub name: String,
    pub key: String,
    pub data_scope: i8,
}

impl From<sys_role::Model> for RoleSimpletVo {
    fn from(value: sys_role::Model) -> Self {
        Self {
            id: value.id,
            name: value.name,
            key: value.key,
            data_scope: value.data_scope,
        }
    }
}

/// 角色关联的资源数据
#[derive(Debug, DerivePartialModel, FromQueryResult, Serialize)]
#[sea_orm(entity = "SysRoleResource")]
pub struct RoleResourceVo {
    pub role_id: i32,
    pub resource_id: i32,
    pub method: String,
    pub allow: bool,
}

/// 角色关联的资源数据的提交模型
#[derive(Debug, Deserialize, Validate)]
pub struct RoleResourceDo {
    pub resource_id: i32,
    #[validate(length(max = 32))]
    pub method: String,
    pub allow: Option<bool>,
}

/// 角色提交的数据
#[derive(Debug, Deserialize, Validate)]
pub struct RoleDo {
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub name: String,
    #[validate(length(min = 3, max = 32), non_control_character)]
    pub key: String,
    #[validate(length(max = 255))]
    #[serde(default)]
    pub description: String,
    pub data_scope: i8,
    pub status: bool,
}

impl Into<sys_role::ActiveModel> for RoleDo {
    fn into(self) -> sys_role::ActiveModel {
        sys_role::ActiveModel {
            name: Set(self.name),
            key: Set(self.key),
            description: Set(self.description),
            status: Set(self.status),
            data_scope: Set(self.data_scope),
            ..Default::default()
        }
    }
}

impl Role {
    ane_macros::model_list!(
        #model=sys_role
        /// 根据条件列出roles
        /// # params
        /// - `name:Option<String>`  role名称 contains
        /// - `key:Option<String>`   role key contains
        /// - `status:Option<bool>`  true启用，false停用
        /// - `del:i8`  0未删除，1已删除
        pub fn list(Name.contains(name:Option<String>), Key.contains(key:Option<String>), Status.eq(status:Option<bool>), DelFlag.eq(del:i8))
    );

    ane_macros::model_fetch!(
        #model=sys_role
        /// 通过id查找role
        /// # params
        /// - `key:String` role的key
        /// - `del:i8`  0未删除，1已删除
        pub fn get_by_id(Id.eq(id:i32), DelFlag.eq(del:i8))
    );

    ane_macros::model_fetch!(
        #model=sys_role
        /// 通过key查找role
        /// # params
        /// - `key:String` role的key
        /// - `del:i8`  0未删除，1已删除
        pub fn get_by_key(Key.eq(key:&str), DelFlag.eq(del:i8))
    );

    /// 获取role的user id
    ///
    /// 连接的表 `sys_user_role` `sys_user`
    /// ## params
    /// - `id: i32` role的id
    /// - `status: Option<bool>` user的status
    #[inline]
    pub async fn get_user_id(
        db: &impl ConnectionTrait,
        id: i32,
        status: Option<bool>,
    ) -> Result<Vec<i32>> {
        sys_user::Entity::find()
            .select_only()
            .column(sys_user::Column::UserId)
            .inner_join(sys_user_role::Entity)
            .filter(sys_user_role::Column::RoleId.eq(id))
            .apply_if(status, |sql, v| sql.filter(sys_user::Column::Status.eq(v)))
            .into_tuple::<i32>()
            .all(db)
            .await
    }

    /// 获取role的user
    ///
    /// 连接的表 `sys_user_role` `sys_user`
    /// ## params
    /// - `id: i32` role的id
    /// - `status: Option<bool>` user的status
    #[inline]
    pub async fn get_user<T>(
        db: &impl ConnectionTrait,
        id: i32,
        status: Option<bool>,
    ) -> Result<Vec<T>>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_user::Entity::find()
            .select_only()
            .column(sys_user::Column::UserId)
            .inner_join(sys_user_role::Entity)
            .filter(sys_user_role::Column::RoleId.eq(id))
            .apply_if(status, |sql, v| sql.filter(sys_user::Column::Status.eq(v)))
            .into_partial_model()
            .all(db)
            .await
    }

    /// 获取role的resource，不检查role是否存在
    ///
    /// 连接的表 `sys_role_resource` `sys_resource`
    /// ## params
    /// - `id: i32` role的id
    /// - `status: Option<bool>` resource的status
    #[inline]
    pub async fn get_resource<T>(
        db: &impl ConnectionTrait,
        id: i32,
        status: Option<bool>,
    ) -> Result<Vec<T>>
    where
        T: PartialModelTrait + Send + Sync,
    {
        sys_role_resource::Entity::find()
            .inner_join(sys_resource::Entity)
            .filter(sys_role_resource::Column::RoleId.eq(id))
            .apply_if(status, |sql, v| {
                sql.filter(sys_resource::Column::Status.eq(v))
            })
            .into_partial_model()
            .all(db)
            .await
    }

    pub async fn create(db: &impl ConnectionTrait, data: RoleDo) -> Result<RoleVo> {
        let mut data: sys_role::ActiveModel = data.into();
        data.del_flag = Set(0);
        data.insert(db).await.map(Into::into)
    }

    /// 根据id更新, 当id不存在时, 抛出错误
    pub async fn update(db: &impl ConnectionTrait, id: i32, data: RoleDo) -> Result<RoleVo> {
        let mut data: sys_role::ActiveModel = data.into();
        data.id = Set(id);
        data.update(db).await.map(Into::into)
    }

    /// 删除角色，和角色关联的用户及资源的关系，建议使用commit事务来执行
    pub async fn delete(db: &impl ConnectionTrait, id: i32) -> Result<sea_orm::DeleteResult> {
        sys_user_role::Entity::delete_many()
            .filter(sys_user_role::Column::RoleId.eq(id))
            .exec(db)
            .await?;
        sys_role_resource::Entity::delete_many()
            .filter(sys_role_resource::Column::RoleId.eq(id))
            .exec(db)
            .await?;
        let res = sys_role::Entity::delete_many()
            .filter(sys_role::Column::Id.eq(id))
            .exec(db)
            .await?;
        Ok(res)
    }

    /// 更新role的resource
    pub async fn update_resource(
        db: &impl ConnectionTrait,
        role_id: i32,
        resources: Vec<RoleResourceDo>,
    ) -> Result<bool> {
        sys_role_resource::Entity::delete_many()
            .filter(sys_role_resource::Column::RoleId.eq(role_id))
            .exec(db)
            .await?;
        let resources = resources
            .into_iter()
            .map(|rr| sys_role_resource::ActiveModel {
                role_id: Set(role_id),
                resource_id: Set(rr.resource_id),
                method: Set(rr.method),
                allow: Set(rr.allow.unwrap_or(true)),
            })
            .collect::<Vec<_>>();
        sys_role_resource::Entity::insert_many(resources)
            .exec(db)
            .await?;
        Ok(true)
    }
}
